using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using MessagePack;

namespace Gj.MapData
{
    public class EventBuilder
    {
        public static ConcurrentQueue<EventBuilder> Queue = new();
        private int _alloc;
        private SubmitData _content;
        private byte _event;

        private StandardExecutor _executor;
        private DefineData _from;
        private string _mapNoId;
        private EventMode _mode;

        public static EventBuilder Get()
        {
            EventBuilder builder = null;
            if (Queue.TryDequeue(out builder))
            {
            }

            if (builder == null) builder = new EventBuilder();
            return builder;
        }

        public static EventBuilder Get(StandardExecutor executor)
        {
            var builder = Get();
            builder._executor = executor;
            builder._mapNoId = executor.CurrentMapNoId;
            return builder;
        }

        internal static void Release(EventBuilder builder)
        {
            if (builder._alloc == 0)
            {
                builder._content = null;
                builder._mode = EventMode.Node;
                builder._alloc = 0;
                builder._event = 0;
                builder._from = null;
                Queue.Enqueue(builder);
            }
            else
            {
                builder._alloc--;
            }
        }

        public EventBuilder Alloc()
        {
            _alloc++;
            return this;
        }

        public EventBuilder BindExecutor(StandardExecutor executor)
        {
            _executor = executor;
            _mapNoId = executor.CurrentMapNoId;
            return this;
        }

        public EventBuilder BindMapNoId(string mapNoId)
        {
            _mapNoId = mapNoId;
            return this;
        }

        internal EventData Data()
        {
            var data = new EventData();
            data.Content = _content;
            data.Mode = _mode;
            Release(this);
            return data;
        }

        internal EventItem Item()
        {
            var data = new EventItem();
            data.Content = MessagePackSerializer.Serialize(_content.Object());
            data.Mode = _mode;
            data.Event = _event;
            Release(this);
            return data;
        }

        public EventBuilder From(DefineData from)
        {
            _from = from;
            return this;
        }

        public EventBuilder Schedule(ScheduleBuilder builder, CheckBuilder check = null)
        {
            var content = builder.Content();
            content.Checks = check?.Data();
            _content = content;
            _mode = EventMode.Schedule;

            return this;
        }

        public EventBuilder Operate(OperateBuilder builder, CheckBuilder check = null)
        {
            var content = builder.Content();
            content.Checks = check?.Data();
            _content = content;
            _mode = EventMode.Operate;
            return this;
        }

        public EventBuilder Task(TaskBuilder builder, CheckBuilder check = null)
        {
            var content = builder.Content();
            content.Checks = check?.Data();
            _content = content;
            _mode = EventMode.Task;
            return this;
        }

        public EventBuilder Bind(byte e)
        {
            _event = e;
            return this;
        }

        public EventBuilder Event(CheckBuilder check = null)
        {
            var content = new EventContent();
            content.Current = new List<byte>();
            content.Events = new List<EventBody>();
            content.Checks = check?.Data();
            _content = content;
            _mode = EventMode.Event;
            return this;
        }

        public EventBuilder AddEvent(byte Event)
        {
            if (_mode != EventMode.Event) throw new Exception("EventBuilder need EventMode: add Event");
            ((EventContent)_content).Current.Add(Event);
            return this;
        }

        public EventBody AddEvent(DataType dataType, object id, byte Event)
        {
            if (_mode != EventMode.Event) throw new Exception("EventBuilder need EventMode: add Event");

            var body = new EventBody();
            body.Data = new BaseData { DataType = dataType, Id = id };
            body.Current = new List<byte> { Event };
            ((EventContent)_content).Events.Add(body);
            return body;
        }

        public EventOperateData ToOperate()
        {
            var eventOperate = new EventOperateData();
            eventOperate.Event = _event;
            eventOperate.Mode = _mode;
            eventOperate.Content = MessagePackSerializer.Serialize(_content.Object());
            Release(this);
            return eventOperate;
        }
    }
}